package com.mrx.xgateway.utils;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONWriter;
import com.mrx.xgateway.model.Config;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.SneakyThrows;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author Mr.X
 * @since 2024-03-10 下午 7:02:30
 */
public class ConfigUtils {

    private static final File CONFIG_FILE = new File("./config/config.json");

    private static final ConfigHolder configHolder = ConfigHolder.of(null, null);

    private static final Logger logger = LoggerFactory.getLogger(ConfigUtils.class);

    private static final ReentrantLock lock = new ReentrantLock();

    private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd-HH-mm-ss-SSSS");

    @SneakyThrows
    public static Collection<Config> writeConfig(Collection<Config> config) {
        Map<String, Config> configMap = config.stream().collect(Collectors.toMap(Config::getPath, Function.identity()));
        Set<Config> oldConfigs = loadConfig();
        boolean configNotChanged = !oldConfigs.isEmpty() && oldConfigs.stream().allMatch(it ->
                Optional.ofNullable(configMap.get(it.getPath()))
                        .map(item -> item.getForward().equals(it.getForward()) && Objects.equals(item.getStripPath(), it.getStripPath()))
                        .orElse(false)
        );
        if (configNotChanged) {
            logger.info("config not changed");
            return config;
        }
        lock.lock();
        try {
            logger.info("writing config");
            File backFile = new File(CONFIG_FILE.getParent(), formatter.format(LocalDateTime.now()) + ".json");
            FileUtils.copyFile(CONFIG_FILE, backFile);
            FileUtils.write(CONFIG_FILE, JSON.toJSONString(config, JSONWriter.Feature.PrettyFormat), StandardCharsets.UTF_8);
        } finally {
            lock.unlock();
        }
        return Collections.unmodifiableCollection(config);
    }

    @SneakyThrows
    public static Set<Config> loadConfig() {
        if (!CONFIG_FILE.exists()) {
            logger.warn("Config file not found");
            FileUtils.createParentDirectories(CONFIG_FILE);
            if (CONFIG_FILE.createNewFile()) {
                logger.info("Creating config file");
            }
            return Collections.emptySet();
        }
        LocalDateTime lastModifyTime = LocalDateTime.ofEpochSecond(
                CONFIG_FILE.lastModified(), 0, ZoneOffset.ofHours(8)
        );
        LocalDateTime lastUpdateTime = configHolder.getLastUpdateTime();
        if (lastUpdateTime == null || lastUpdateTime.isBefore(lastModifyTime)) {
            configHolder.setConfig(loadConfig0());
            configHolder.setLastUpdateTime(lastModifyTime);
        }
        return Collections.unmodifiableSet(configHolder.config);
    }

    @SneakyThrows
    private static Set<Config> loadConfig0() {
        lock.lock();
        try {
            logger.info("loading config");
            List<Config> configs = JSONArray.parseArray(Files.readString(CONFIG_FILE.toPath()), Config.class);
            return new HashSet<>(configs == null ? Collections.emptySet() : configs);
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        loadConfig();
        loadConfig();
    }

    @Data
    @AllArgsConstructor(staticName = "of")
    private static final class ConfigHolder {
        private LocalDateTime lastUpdateTime;
        private Set<Config> config;
    }

}
